fn main() {
    dbg!(
        rv_plic::Priority::<32>::from(0), // good
        // rv_plic::Priority::<31>::from(0), // bad: panic with invalid priority
        fix::Priority::<31>::from(0),        // good
        fix::Priority::<31>::from(1 << 30),  // good
        fix::Priority::<32>::from(u32::MAX), // good
    );
}

mod fix {
    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
    pub struct Priority<const BIT: usize>(pub(crate) u32);

    impl<const BIT: usize> From<u32> for Priority<BIT> {
        fn from(priority: u32) -> Self {
            Priority(crate::prio(priority, BIT).expect("invalid priority"))
        }
    }
}

#[test]
fn test() {
    assert_eq!(prio(0, 0), Some(0));

    assert_eq!(prio(1, 1), Some(1));
    assert_eq!(prio(2, 1), None);

    assert_eq!(prio(0, 2), Some(0));
    assert_eq!(prio(1, 2), Some(1));
    assert_eq!(prio(2, 2), Some(2));
    assert_eq!(prio(3, 2), Some(3));
    assert_eq!(prio(1 << 2, 2), None);

    let n = (1 << 30) + (1 << 29);
    assert_eq!(prio(n, 30), None);
    assert_eq!(prio(n, 31), Some(n));
    assert_eq!(prio(n, 32), Some(n));
}

fn prio(n: u32, bits: usize) -> Option<u32> {
    match bits {
        0..=31 if n < 1u32 << bits => Some(n),
        32 => Some(n),
        _ => None,
    }
}
